`timescale 1ns / 1ps
/*
 Copyright 2020 Sean Xiao, jxzsxsp@qq.com
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

module csr_mstatus
(
    input         sys_clk,

    input         i_acc_dis,
    input         i_mret_ena,
    input         i_status_ena,

    input  [11:0] i_csr_addr,
    input  [31:0] i_csr_val,
    input         i_csr_wen,

    output [31:0] o_mstatus,

    input         rst_n
);
/*
o_mstatus  Machine Status Register     RW

31     30                                               23  22    21    20    19    18     17
------------------------------------------------------------------------------------------------
SD    |                      WPRI                         | TSR | TW  | TVM | MXR | SUM | MPRV |
------------------------------------------------------------------------------------------------
1                             8                             1      1     1     1     1     1

16    15 14     13 12      11 10   9   8     7      6      5      4      3      2     1     0
------------------------------------------------------------------------------------------------
XS[1:0] | FS[1:0] | MPP[1:0] | MPRI | SPP | MPIE | WPRI | SPIE | UPIE | MIE | WPRI | SIE | UIE |
------------------------------------------------------------------------------------------------
2           2          2        2     1      1      1      1      1      1      1     1     1

MIE:    MIE <= 1,  enable all Machine Mode interrupt; MIE <= 0, during irq/exception happend
MPIE:   MPIE <= MIE before irq/exception,when exception happened; MIE <= MPIE, after irq/exception.

*/

wire wbck_csr_wen = i_csr_wen  & (~i_acc_dis);
wire sel_mstatus = (i_csr_addr == 12'h300);

wire wr_mstatus = sel_mstatus & wbck_csr_wen;
wire status_mpie_r;
wire status_mie_r;
    // The MPIE fields will be updates when: 
wire status_mpie_ena  = 
        // The CSR is written by CSR instructions
        wr_mstatus |
        // The MRET instruction committed
        i_mret_ena |
        // The Trap is taken
        i_status_ena;

wire status_mpie_nxt  = 
    //   See Priv SPEC:
    //       When a trap is taken from privilege mode y into privilege
    //       mode x, xPIE is set to the value of xIE;
    // So, When the Trap is taken, the MPIE is updated with the current MIE value
    i_status_ena ? status_mie_r :
    //   See Priv SPEC:
    //       When executing an xRET instruction, supposing xPP holds the value y, xIE
    //       is set to xPIE; the privilege mode is changed to y; 
    //       xPIE is set to 1;
    // So, When the MRET instruction committed, the MPIE is updated with 1
    i_mret_ena ? 1'b1 :
    // When the CSR is written by CSR instructions
    wr_mstatus ? i_csr_val[7] : // MPIE is in field 7 of o_mstatus
                 status_mpie_r; // Unchanged 

yue_dfflr #(1) status_mpie_dfflr (status_mpie_ena, status_mpie_nxt, status_mpie_r, sys_clk, rst_n);
//////////////////////////
// Implement MIE field
//
    // The MIE Feilds will be updates same as MPIE
wire status_mie_ena  = status_mpie_ena; 
wire status_mie_nxt  = 
    //   See Priv SPEC:
    //       When a trap is taken from privilege mode y into privilege
    //       mode x, xPIE is set to the value of xIE,
    //       xIE is set to 0;
    // So, When the Trap is taken, the MIE is updated with 0
        i_status_ena ? 1'b0 :
    //   See Priv SPEC:
    //       When executing an xRET instruction, supposing xPP holds the value y, xIE
    //       is set to xPIE; the privilege mode is changed to y, xPIE is set to 1;
    // So, When the MRET instruction committed, the MIE is updated with MPIE
        i_mret_ena ? status_mpie_r :
    // When the CSR is written by CSR instructions
        wr_mstatus ? i_csr_val[3] : // MIE is in field 3 of o_mstatus
                  status_mie_r; // Unchanged 

yue_dfflr #(1) status_mie_dfflr (status_mie_ena, status_mie_nxt, status_mie_r, sys_clk, rst_n);

//////////////////////////
// Implement SD field
//
//  See Priv SPEC:
//    The SD bit is read-only 
//    And is set when either the FS or XS bits encode a Dirty
//      state (i.e., SD=((FS==11) OR (XS==11))).
wire [1:0] status_fs_r;
wire [1:0] status_xs_r;
wire status_sd_r = (status_fs_r == 2'b11) | (status_xs_r == 2'b11);


//////////////////////////
// Implement XS field
//
//  See Priv SPEC:
//    XS field is read-only
//    The XS field represents a summary of all extensions' status
//  But in E200 we implement XS exactly same as FS to make it usable by software to 
//   disable extended accelerators
`ifndef FII_HAS_EAI
   // If no EAI coprocessor interface configured, the XS is just hardwired to 0
    assign status_xs_r = 2'b0; 
    assign eai_xs_off = 1'b0;// We just make this signal to 0
`endif

//////////////////////////
// Implement FS field
//

`ifndef FII_HAS_FPU
   // If no FPU configured, the FS is just hardwired to 0
    assign status_fs_r = 2'b0; 
`endif

//////////////////////////
// Pack to the full o_mstatus register
//
wire [31:0] status_tmp;
assign status_tmp[31]    = status_sd_r;     // SD
assign status_tmp[30:23] = 8'b0;            // Reserved
assign status_tmp[22:17] = 6'b0;            // TSR--MPRV
assign status_tmp[16:15] = status_xs_r;     // XS
assign status_tmp[14:13] = status_fs_r;     // FS
assign status_tmp[12:11] = 2'b11;           // MPP 
assign status_tmp[10:9]  = 2'b0;            // Reserved
assign status_tmp[8]     = 1'b0;            // SPP
assign status_tmp[7]     = status_mpie_r;   // MPIE
assign status_tmp[6]     = 1'b0;            // Reserved
assign status_tmp[5]     = 1'b0;            // SPIE 
assign status_tmp[4]     = 1'b0;            // UPIE 
assign status_tmp[3]     = status_mie_r;    // MIE
assign status_tmp[2]     = 1'b0;            // Reserved
assign status_tmp[1]     = 1'b0;            // SIE 
assign status_tmp[0]     = 1'b0;            // UIE 



assign  o_mstatus = status_tmp;


endmodule
